<template>
  <view class="vastPopover" :style="{'--theme-bg-color':bgStyleColor}">
    <!--  点击处  -->
    <view @click.stop="handleClick" class="vast-button-popover">
      <slot></slot>
    </view>
    <!--  弹出层  -->
    <view class="vast-popover" v-show="inited" ref="vast-transition" :class="[classes,`vast-popover-${placement}`]"
          :style="[mergeStyle]" @touchmove="noop">
      <!--   箭头   -->
      <view class="vast-popover-arrow" :style="[arrowStyle]"
            :class="[{
							'vast_popper__up':currentPlacement.indexOf('bottom')===0,
							'vast_popper__arrow':currentPlacement.indexOf('top')===0,
							'vast_popper__right':currentPlacement.indexOf('right')===0,
							'vast_popper__left':currentPlacement.indexOf('left')===0,
						}]">
      </view>
      <!--   内容    -->
      <slot name="content">
        <view @click.stop="actionAction(item)"
              v-for="(item,index) in options"
              class="vast-popover__action"
              :class="[{'dark__action':theme==='dark'}]"
              :key="index">
          <view class="vast-popover__action-text">
            {{ item.label }}
          </view>
        </view>
      </slot>
    </view>
    <!--   遮罩  -->
    <view class="mark" @click.stop.prevent="close()" @touchmove.stop.prevent="noop"
          :class="[{'mark--filter':backdropFilter}]" v-show="inited"/>
  </view>
</template>

<script>
const tranClass = {
  enter: "vast-fade-zoom-enter vast-fade-zoom-enter-active",
  'enter-to': "vast-fade-zoom-enter-to vast-fade-zoom-enter-active",
  leave: "vast-fade-zoom-leave vast-fade-zoom-leave-active",
  'leave-to': "vast-fade-zoom-leave-to vast-fade-zoom-leave-active",
}
export default {
  name: "VastPopover",
  props: {
    options: {
      type: Array,
      default: () => []
    },
    placement: {
      type: String,
      default: 'auto'
    },
    bgColor: {
      type: String,
    },
    // 主题色 light dark
    theme: {
      type: String,
      default: 'light'
    },
    // 是否开启背景模糊
    backdropFilter: {
      type: Boolean,
      default: false
    }
  },
  watch: {
    show: {
      handler(newVal) {
        newVal ? this.vueEnter() : this.vueLeave()
      },
      // 表示同时监听初始化时的props的show的意思
      immediate: true
    }
  },
  data() {
    return {
      show: false,
      inited: false, // 是否显示/隐藏组件
      classes: '', // 应用的类名
      display: false, // 组件是否展示
      duration: 100,
      popoverStyle: {},
      arrowOldStyle: {},
      calcPosition: '',
      currentPlacement: 'bottom'
    };
  },
  computed: {
    windowHeight() {
      return uni.getSystemInfoSync().windowHeight
    },
    windowWidth() {
      return uni.getSystemInfoSync().windowWidth
    },
    bgStyleColor() {
      if (this.bgColor) {
        return this.bgColor
      }
      if (this.theme === 'light') {
        return '#ffffff'
      }
      if (this.theme === 'dark') {
        return '#4a4a4a'
      }
    },

    mergeStyle() {
      return {
        transitionDuration: `${this.duration}ms`,
        transitionTimingFunction: `ease-out`,
        ...this.popoverStyle
      }
    },
    arrowStyle() {
      return {
        ...this.arrowOldStyle
      }
    }
  },
  methods: {
    handleClick() {
      if (this.show) {
        this.show = false
      } else {
        this.show = true
      }
      this.$emit('handleClick', this.show)
    },
    close() {
      this.show = false
    },
    actionAction(item) {
      this.$emit('select', item)
      this.show = false
    },
    sleep(value) {
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve()
        }, value)
      })
    },
    vueEnter() {
      this.inited = true
      this.getPosition()
      this.classes = tranClass.enter
      this.$nextTick(async () => {
        await this.sleep(30)
        this.classes = tranClass['enter-to']

      })
    },
    vueLeave() {
      this.classes = tranClass.leave

      this.$nextTick(async () => {
        this.classes = tranClass['leave-to']
        await this.sleep(120)
        this.inited = false
      })
    },
    // 阻止事件冒泡
    preventEvent(e) {
      e && typeof (e.stopPropagation) === 'function' && e.stopPropagation()
    },
    computePlacement(triggerRect, popoverRect) {
      // 解构触发元素的位置信息
      let {left, bottom, right, top, width, height} = triggerRect

      if (this.placement === null || this.placement === undefined || this.placement === '' || this.placement === 'auto') {
        // 安全距离，距离窗口边缘至少10px
        const safeDistance = 10

        // 计算各个方向的可用空间
        const availableSpace = {
          bottom: this.windowHeight - bottom,
          top: top,
          left: left,
          right: this.windowWidth - right
        }

        // 最小可见比例（允许部分遮挡，只要能显示70%的内容即可）
        const minVisibleRatio = 0.7
        const minVisibleHeight = popoverRect.height * minVisibleRatio
        const minVisibleWidth = popoverRect.width * minVisibleRatio

        // 检查各个方向是否有足够空间（允许部分遮挡）
        const hasSpace = {
          bottom: availableSpace.bottom >= minVisibleHeight,
          top: availableSpace.top >= minVisibleHeight,
          left: availableSpace.left >= minVisibleWidth,
          right: availableSpace.right >= minVisibleWidth
        }

        // 计算每个方向的适用性得分
        const scores = []

        // 左侧位置的得分计算
        if (hasSpace.left) {
          const leftScore = {
            name: 'left',
            score: (availableSpace.left / popoverRect.width) * 100
          }
          // 如果在屏幕右侧，增加左侧展示的优先级
          if (right > this.windowWidth * 0.6) {
            leftScore.score += 50
          }
          scores.push(leftScore)
        }

        // 右侧位置的得分计算
        if (hasSpace.right) {
          const rightScore = {
            name: 'right',
            score: (availableSpace.right / popoverRect.width) * 100
          }
          // 如果在屏幕左侧，增加右侧展示的优先级
          if (left < this.windowWidth * 0.4) {
            rightScore.score += 50
          }
          scores.push(rightScore)
        }

        // 顶部位置的得分计算
        if (hasSpace.top) {
          const topScore = {
            name: 'top',
            score: (availableSpace.top / popoverRect.height) * 100
          }
          // 如果在屏幕下方，增加顶部展示的优先级
          if (bottom > this.windowHeight * 0.6) {
            topScore.score += 50
          }
          scores.push(topScore)
        }

        // 底部位置的得分计算
        if (hasSpace.bottom) {
          const bottomScore = {
            name: 'bottom',
            score: (availableSpace.bottom / popoverRect.height) * 100
          }
          // 如果在屏幕上方，增加底部展示的优先级
          if (top < this.windowHeight * 0.4) {
            bottomScore.score += 50
          }
          scores.push(bottomScore)
        }

        // 特殊处理：右上角情况
        if (right > this.windowWidth * 0.7 && top < this.windowHeight * 0.3) {
          // 如果左侧空间足够显示最小宽度，优先使用左侧展示
          if (availableSpace.left >= minVisibleWidth) {
            this.currentPlacement = 'bottom-end'
            return this.currentPlacement
          }
        }

        // 特殊处理：左上角情况
        if (left < this.windowWidth * 0.3 && top < this.windowHeight * 0.3) {
          // 如果右侧空间足够，优先使用右侧展示
          if (availableSpace.right >= minVisibleWidth) {
            this.currentPlacement = 'bottom-start'
            return this.currentPlacement
          }
        }
        // 特殊处理：右下角情况
        if (right > this.windowWidth * 0.7 && bottom > this.windowHeight * 0.7) {
          // 如果左侧空间足够显示最小宽度，优先使用左侧展示
          if (availableSpace.left >= minVisibleWidth) {
            this.currentPlacement = 'top-end'
            return this.currentPlacement
          }
        }
        // 特殊处理：左下角情况
        if (left < this.windowWidth * 0.3 && bottom > this.windowHeight * 0.7) {
          // 如果右侧空间足够，优先使用右侧展示
          if (availableSpace.right >= minVisibleWidth) {
            this.currentPlacement = 'top-start'
            return this.currentPlacement
          }
        }

        // 如果有可用位置，选择得分最高的
        if (scores.length > 0) {
          // 根据位置调整最终的placement
          const bestPlacement = scores.sort((a, b) => b.score - a.score)[0]
          let finalPlacement = bestPlacement.name

          // 根据位置调整对齐方式
          if (finalPlacement === 'left' || finalPlacement === 'right') {
            // 如果太靠近顶部，使用start
            if (top < popoverRect.height / 2) {
              finalPlacement += '-start'
            }
            // 如果太靠近底部，使用end
            else if (this.windowHeight - bottom < popoverRect.height / 2) {
              finalPlacement += '-end'
            }
          } else if (finalPlacement === 'top' || finalPlacement === 'bottom') {
            // 如果太靠近左边，使用start
            if (left < popoverRect.width / 2) {
              finalPlacement += '-start'
            }
            // 如果太靠近右边，使用end
            else if (this.windowWidth - right < popoverRect.width / 2) {
              finalPlacement += '-end'
            }
          }

          this.currentPlacement = finalPlacement
        } else {
          // 如果没有完全理想的位置，选择空间最大的方向
          const directions = [
            {name: 'bottom', space: availableSpace.bottom},
            {name: 'top', space: availableSpace.top},
            {name: 'left', space: availableSpace.left},
            {name: 'right', space: availableSpace.right}
          ]

          // 获取最大空间的方向
          const bestDirection = directions.sort((a, b) => b.space - a.space)[0]
          let finalPlacement = bestDirection.name

          // 根据位置调整对齐方式
          if (finalPlacement === 'left' || finalPlacement === 'right') {
            if (top < popoverRect.height / 2) {
              finalPlacement += '-start'
            } else if (this.windowHeight - bottom < popoverRect.height / 2) {
              finalPlacement += '-end'
            }
          } else if (finalPlacement === 'top' || finalPlacement === 'bottom') {
            if (left < popoverRect.width / 2) {
              finalPlacement += '-start'
            } else if (this.windowWidth - right < popoverRect.width / 2) {
              finalPlacement += '-end'
            }
          }

          this.currentPlacement = finalPlacement
        }

        return this.currentPlacement
      }
      this.currentPlacement = this.placement
      return this.placement
    },
    getPosition() {
      return new Promise((resolve) => {
        this.$nextTick(() => {
          let nodesRef = uni.createSelectorQuery().in(this).selectAll('.vast-button-popover,.vast-popover')
          nodesRef.boundingClientRect(async (data) => {
            const [triggerRect, popoverRect] = data
            let {left, bottom, right, top, width, height} = triggerRect
            let popoverStyle = {}
            let arrowOldStyle = {}

            switch (this.computePlacement(triggerRect, popoverRect)) {
              case 'top':
                if (popoverRect.width > width) {
                  popoverStyle.left = `-${(popoverRect.width - width) / 2}px`
                } else {
                  popoverStyle.left = `${Math.abs(popoverRect.width - width) / 2}px`
                }
                popoverStyle.bottom = `${height + 8}px`
                arrowOldStyle.left = (popoverRect.width / 2 - 6) + 'px'
                break;
              case 'top-start':
                popoverStyle.left = `0px`
                popoverStyle.bottom = `${height + 8}px`
                arrowOldStyle.left = '16px'
                break;
              case 'top-end':
                popoverStyle.right = `0px`
                popoverStyle.bottom = `${height + 8}px`
                arrowOldStyle.right = '16px'
                break;
              case 'bottom':
                if (popoverRect.width > width) {
                  popoverStyle.left = `-${(popoverRect.width - width) / 2}px`
                } else {
                  popoverStyle.left = `${Math.abs(popoverRect.width - width) / 2}px`
                }
                popoverStyle.top = `${height + 8}px`
                arrowOldStyle.left = (popoverRect.width / 2 - 6) + 'px'
                break;
              case 'bottom-start':
                popoverStyle.top = `${height + 8}px`
                popoverStyle.left = `0px`
                arrowOldStyle.left = '16px'
                break;
              case 'bottom-end':
                popoverStyle.top = `${height + 8}px`
                popoverStyle.right = `0px`
                arrowOldStyle.right = '16px'
                break;
              case 'right':
                popoverStyle.left = `${width + 8}px`
                if (popoverRect.height > height) {
                  popoverStyle.top = `-${(popoverRect.height - height) / 2}px`
                } else {
                  popoverStyle.top = `${Math.abs((popoverRect.height - height) / 2)}px`
                }
                arrowOldStyle.top = `${popoverRect.height / 2 - 6}px`
                break;
              case 'right-start':
                popoverStyle.left = `${width + 8}px`
                popoverStyle.top = `0px`
                arrowOldStyle.top = `8px`
                break;
              case 'right-end':
                popoverStyle.left = `${width + 8}px`
                popoverStyle.bottom = `0px`
                arrowOldStyle.bottom = `8px`
                break;
              case 'left':
                popoverStyle.right = `${width + 8}px`
                if (popoverRect.height > height) {
                  popoverStyle.top = `-${(popoverRect.height - height) / 2}px`
                } else {
                  popoverStyle.top = `${Math.abs((popoverRect.height - height) / 2)}px`
                }
                arrowOldStyle.top = `${popoverRect.height / 2 - 6}px`
                break;
              case 'left-start':
                popoverStyle.right = `${width + 8}px`
                popoverStyle.top = `0px`
                arrowOldStyle.top = `8px`
                break;

              case 'left-end':
                popoverStyle.right = `${width + 8}px`
                popoverStyle.bottom = `0px`
                arrowOldStyle.bottom = `8px`
                break;
            }

            this.popoverStyle = popoverStyle
            this.arrowOldStyle = arrowOldStyle
            resolve()
          }).exec()
        })
      })
    },
    noop(e) {
      this.preventEvent(e)
    }
  }
}
</script>

<style lang="scss" scoped>
$theme-bg-color: var(--theme-bg-color);
.vastPopover {
  position: relative;
}

.vast-button-popover {
  display: inline-block;
}

.vast-popover {
  border-radius: 8px;
  z-index: 1999;
  position: absolute;
  background-color: $theme-bg-color;
  box-shadow: 0 2px 12px #3232331f;

}

.vast-popover-top {
  transform-origin: 50% bottom;
}

.vast-popover-top-start {
  transform-origin: 50% bottom;
}

.vast-popover-top-end {
  transform-origin: 0 bottom;
}

.vast-popover-bottom {
  transform-origin: 50% 0;
}

.vast-popover-bottom-end {
  transform-origin: 100% 0;
}

.vast-popover-bottom-start {
  transform-origin: 0 0;
}

.vast-popover-right {
  transform-origin: left 50%;
}

.vast-popover-right-start {
  transform-origin: left 0;
}

.vast-popover-right-end {
  transform-origin: left 100%;
}

.vast-popover-left {
  transform-origin: right 50%;
}

.vast-popover-left-start {
  transform-origin: right 0;
}

.vast-popover-left-end {
  transform-origin: right 100%;
}

.vast-popover-arrow {
  position: absolute;
  width: 0;
  height: 0;
  border-color: transparent;
  border-style: solid;
  border-width: 6px;
  color: $theme-bg-color;
}

.vast_popper__up {
  border-top-width: 0;
  border-bottom-color: currentColor;
  top: -6px;
}

.vast_popper__right {
  border-left-width: 0;
  border-right-color: currentColor;
  left: -5px;
}

.vast_popper__left {
  border-right-width: 0;
  border-left-color: currentColor;
  right: -5px;
}

.vast_popper__arrow {
  border-bottom-width: 0;
  border-top-color: currentColor;
  bottom: -6px;
}

.vast-popover__action {
  position: relative;
  display: flex;
  align-items: center;
  box-sizing: border-box;
  height: 88rpx;
  padding: 0 40rpx;
  font-size: 32rpx;
  cursor: pointer;
}

.vast-popover__action-text {
  display: flex;
  flex: 1;
  align-items: center;
  justify-content: center;
  height: 100%;
  padding: 0 30rpx;
  border-bottom: 1rpx solid #ebedf0;
  word-wrap: break-word;
  white-space: nowrap;
}

.vast-popover__action:last-child {
  .vast-popover__action-text {
    border-bottom: none;
  }
}

.dark__action {
  color: white;

  .vast-popover__action-text {
    border-bottom: 1rpx solid #ebedf033
  }
}

.vast-popover__action {
  padding: 0 20rpx;
  border-right: 1rpx solid #ebedf0;
}

.vast-popover__action-text {
  padding: 0;
  //border-right:1rpx solid #ebedf0;
}

$u-zoom-scale: scale(0.95);

.vast-fade-enter-active,
.vast-fade-leave-active {
  transition-property: opacity;
}

.vast-fade-enter,
.vast-fade-leave-to {
  opacity: 0
}

.vast-fade-zoom-enter,
.vast-fade-zoom-leave-to {
  transform: $u-zoom-scale;
  opacity: 0;
}

.vast-fade-zoom-enter-active,
.vast-fade-zoom-leave-active {
  transition-property: transform, opacity;
}

.vast-fade-down-enter-active,
.vast-fade-down-leave-active,
.vast-fade-left-enter-active,
.vast-fade-left-leave-active,
.vast-fade-right-enter-active,
.vast-fade-right-leave-active,
.vast-fade-up-enter-active,
.vast-fade-up-leave-active {
  transition-property: opacity, transform;
}

.vast-fade-up-enter,
.vast-fade-up-leave-to {
  transform: translate3d(0, 100%, 0);
  opacity: 0
}

.vast-fade-down-enter,
.vast-fade-down-leave-to {
  transform: translate3d(0, -100%, 0);
  opacity: 0
}

.vast-fade-left-enter,
.vast-fade-left-leave-to {
  transform: translate3d(-100%, 0, 0);
  opacity: 0
}

.vast-fade-right-enter,
.vast-fade-right-leave-to {
  transform: translate3d(100%, 0, 0);
  opacity: 0
}


.vast-popover__action {
  &:active {
    background-color: rgba(0, 0, 0, 0.2);
  }

  &--disabled {
    color: var(--van-popover-dark-action-disabled-text-color);

    &:active {
      background-color: transparent;
    }
  }
}

.mark {
  position: fixed;
  left: 0;
  top: 0;
  width: 100vw;
  height: 100vh;
  z-index: 1998;
  background: rgba(0, 0, 0, 0.1);
  transition: opacity 0.1s ease;
  opacity: 1;
  pointer-events: auto;

  &--filter {
    backdrop-filter: blur(1px);
    -webkit-backdrop-filter: blur(1px);
  }
}

</style>
